//
//    Tokaplot
//    https://code.google.com/p/tokaplot/
//
//    License: GPL v3
//    https://code.google.com/p/tokaplot/wiki/License
//
//    This file is part of Tokaplot.
//
//    Tokaplot is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Tokaplot is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Tokaplot.  If not, see <http://www.gnu.org/licenses/>
//

package com.tokaplot;

import java.text.DecimalFormat;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Paint.Align;
import android.view.Display;
import android.view.WindowManager;
import android.widget.RemoteViews;
//import android.util.Log;

public class Render {
	
	//private static final String TAG = "Tokaplot";
	
	private Bitmap foreBMP = null;
	private Bitmap dataBMP = null;
	private Bitmap backBMP = null;
	
	private int rWidth 		= 1;
	private int dWidth 		= 1;
	private int rHeight 	= 1;
	private int dHeight 	= 1;
	private int yPadding 	= 5;
	
	private int last_plot_Back_Col1 = -1;
	private int last_plot_Back_Col2 = -1;	
	private int last_grid_Type		= -1;
	private int last_grid_Col		= -1;
	private int last_grid_Style		= -1;
	private int last_text_Col		= -1;
	private long last_samp_Rate     = -1;
	
	private int wTextView1, wTextView2, wBackground, wForeground;

	private static DecimalFormat ShortFloat = new DecimalFormat("#0.00");
	private static DecimalFormat VeryShortFloat = new DecimalFormat("#0.0");
	
	private Path rPath = null;
	
	Context rContext;
	
	Render(WidgetInfo wInfo, Context context) {
		
		rContext = context;
		updateconfig(wInfo);

	}
	
	public void updateconfig(WidgetInfo wInfo) {

		Resources res = rContext.getResources();
		Display display = ((WindowManager) rContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
				
		// Set render sizes depending on orientation and widget size
		float scale = 1.0f/rContext.getResources().getDisplayMetrics().density;
		if (display.getHeight()>display.getWidth()) {
			if (wInfo.is22()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_22b_port_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_22b_port_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_22f_port_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_22f_port_width));
			} else if (wInfo.is41()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_41b_port_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_41b_port_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_41f_port_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_41f_port_width));
			} else if (wInfo.is42()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_42b_port_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_42b_port_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_42f_port_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_42f_port_width));
			} else {
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_21b_port_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_21b_port_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_21f_port_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_21f_port_width));				
			}
		} else {
			if (wInfo.is22()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_22b_land_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_22b_land_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_22f_land_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_22f_land_width));
			} else if (wInfo.is41()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_41b_land_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_41b_land_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_41f_land_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_41f_land_width));
			} else if (wInfo.is42()) {				
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_42b_land_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_42b_land_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_42f_land_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_42f_land_width));
			} else {
				rHeight = (int)(scale*res.getDimension(R.dimen.layout_21b_land_height));
				rWidth 	= (int)(scale*res.getDimension(R.dimen.layout_21b_land_width));
				dHeight = (int)(scale*res.getDimension(R.dimen.layout_21f_land_height));
				dWidth 	= (int)(scale*res.getDimension(R.dimen.layout_21f_land_width));				
			}				
		}
		
		// Create bitmaps
		foreBMP = Bitmap.createBitmap(dWidth + 2, dHeight + yPadding*2 + 2, Bitmap.Config.ARGB_8888);
		dataBMP = Bitmap.createBitmap(dWidth + 2, dHeight + yPadding*2 + 2, Bitmap.Config.ARGB_8888);
		backBMP = Bitmap.createBitmap(rWidth, rHeight, Bitmap.Config.ARGB_8888);
        
		// Create paths
		rPath = new Path();
		rPath.incReserve(dWidth + 2);
		
		// Set id indexes
		if (wInfo.is22()) {				
			wTextView1 = R.id.widget_textview1_22;
			wTextView2 = R.id.widget_textview2_22;
			wBackground = R.id.widget_background_22;
			wForeground = R.id.widget_foreground_22;
		} else if (wInfo.is41()) {
			wTextView1 = R.id.widget_textview1_41;
			wTextView2 = R.id.widget_textview2_41;
			wBackground = R.id.widget_background_41;
			wForeground = R.id.widget_foreground_41;
		} else if (wInfo.is42()) {
			wTextView1 = R.id.widget_textview1_42;
			wTextView2 = R.id.widget_textview2_42;
			wBackground = R.id.widget_background_42;
			wForeground = R.id.widget_foreground_42;
		} else {
			wTextView1 = R.id.widget_textview1_21;
			wTextView2 = R.id.widget_textview2_21;
			wBackground = R.id.widget_background_21;
			wForeground = R.id.widget_foreground_21;			
		}
	}
	
	public void update(RemoteViews remoteViews, WidgetInfo wInfo, RenderInfo plot1, RenderInfo plot2, boolean isPaused) {
		
		int i;
		
		// Check for config updates
		if (wInfo.getNeedsUpdate() == true) {
			updateconfig(wInfo);
			wInfo.unsetNeedsUpdate();
			updateBackground(wInfo);
			updateForeground(wInfo);
		}
		
        // Render background
		if (last_plot_Back_Col1 != wInfo.getBackCol1() || last_plot_Back_Col2 != wInfo.getBackCol2()) {
			updateBackground(wInfo);
			last_plot_Back_Col1 = wInfo.getBackCol1();
			last_plot_Back_Col2 = wInfo.getBackCol2();
		}
        
        // Render foreground
		if ((last_samp_Rate != wInfo.getSampRate()) || 
				(last_grid_Type != wInfo.getGridType()) ||
				(last_grid_Col != wInfo.getGridCol()) ||
				(last_grid_Style != wInfo.getGridStyle()) ||
				(last_text_Col != wInfo.getTextCol())) {
	        updateForeground(wInfo);
	        last_grid_Type = wInfo.getGridType();
	        last_samp_Rate = wInfo.getSampRate();
	        last_grid_Col = wInfo.getGridCol();
	        last_grid_Style = wInfo.getGridStyle();
	        last_text_Col = wInfo.getTextCol();
		}
		
    	// Render data
        dataBMP.eraseColor(0x00FFFFFF);
        Canvas cFront = new Canvas(dataBMP);
        cFront.drawBitmap(foreBMP, 0, 0, null);
        
        // Plot 1
		if (wInfo.getType1() != TokaplotService.s_none) {
			renderLine(cFront, wInfo.getCol1(), wInfo.getStyle1(), plot1);
			
			if (isPaused) {
				remoteViews.setTextViewText(wTextView1, "Paused");
			} else {
				if (wInfo.getShowText1()) {
					
					// Write text string
					remoteViews.setTextColor(wTextView1, wInfo.getCol1()); 
					if (Math.abs(plot1.rCurrent)<TokaplotService.maxVal) {
						remoteViews.setTextViewText(wTextView1, wInfo.getType1() + " " + ShortFloat.format(plot1.rCurrent));
					} else {
						remoteViews.setTextViewText(wTextView1, wInfo.getType1() + " - ");
					}
					
				} else {
					remoteViews.setTextViewText(wTextView1, "");
				}
			}
		} else {
			remoteViews.setTextViewText(wTextView1, "");
		}
		
		// Plot 2
		if (wInfo.getType2() != TokaplotService.s_none) {			
			renderLine(cFront, wInfo.getCol2(), wInfo.getStyle2(), plot2);
				
			if (isPaused) {
				remoteViews.setTextViewText(wTextView2, "Paused");
			} else {				
				if (wInfo.getShowText2()) {
					
					// Write text string
					remoteViews.setTextColor(wTextView2, wInfo.getCol2()); 
					if (Math.abs(plot2.rCurrent)<TokaplotService.maxVal) {
						remoteViews.setTextViewText(wTextView2, wInfo.getType2() + " " + ShortFloat.format(plot2.rCurrent));
					} else {
						remoteViews.setTextViewText(wTextView2, wInfo.getType2() + " - ");
					}
					
				} else {
					remoteViews.setTextViewText(wTextView2, "");
				}
			}
		} else {
			remoteViews.setTextViewText(wTextView2, "");
		}
        
		// Render range
        if (wInfo.getShowMaxMin()) {
        	int delta = 20;
        	float y, dy;
        	
    		// Set paint
            Paint pt = new Paint(Paint.ANTI_ALIAS_FLAG);
            if (wInfo.is42())
            	pt.setTextSize(12.0f);
            else
            	pt.setTextSize(9.0f);
            
             // Render range for plot 1
            if (Math.abs(plot1.rMax)<TokaplotService.maxVal && Math.abs(plot1.rMin)<TokaplotService.maxVal) {
	            y = plot1.rMax;
	            dy = ((float)delta*(plot1.rMax-plot1.rMin)/dHeight);
	            pt.setTextAlign(Align.LEFT);
	            for (i=0;i<=dHeight;i+=delta) {
	            	pt.setColor(0x60000000);
	        		cFront.drawText(VeryShortFloat.format(y+0.01f), 2, yPadding + i+6, pt);
	        		pt.setColor(wInfo.getCol1());
	        		cFront.drawText(VeryShortFloat.format(y+0.01f), 1, yPadding + i+5, pt);
	        		y-=dy;
	        	}
            }
            
            // Render range for plot 2
            if (Math.abs(plot2.rMax)<TokaplotService.maxVal && Math.abs(plot2.rMin)<TokaplotService.maxVal) {
	            y = plot2.rMax;
	            dy = ((float)delta*(plot2.rMax-plot2.rMin)/dHeight);
	            pt.setTextAlign(Align.RIGHT);
	            for (i=0;i<=dHeight;i+=delta) {
	            	pt.setColor(0x60000000);
	            	cFront.drawText(VeryShortFloat.format(y+0.01f), dWidth  , yPadding + i+6, pt);
	        		pt.setColor(wInfo.getCol2());
	        		cFront.drawText(VeryShortFloat.format(y+0.01f), dWidth-1, yPadding + i+5, pt);
	        		y-=dy;
	        	}
            }
        }
        
		remoteViews.setImageViewBitmap(wBackground, backBMP);
		remoteViews.setImageViewBitmap(wForeground, dataBMP); // These two lines cause a sawtooth memory pattern
		
	}
	private void updateBackground(WidgetInfo wInfo) {

		// Erase background
        backBMP.eraseColor(0x00FFFFFF);
        Canvas cBack = new Canvas(backBMP);
        
        // Draw background        	
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setShader(new RadialGradient(rWidth, 0.0f, rHeight, wInfo.getBackCol1(), wInfo.getBackCol2(), Shader.TileMode.CLAMP));
        cBack.drawRoundRect(new RectF(2.0f, 2.0f, rWidth-2, rHeight-2), 5.0F, 5.0F, p);
	}
	private void updateForeground(WidgetInfo wInfo) {
		
		// Erase foreground
        foreBMP.eraseColor(0x00FFFFFF);
        Canvas cFront = new Canvas(foreBMP);
        
        int gType = wInfo.getGridType();
        boolean useFine 	= ((gType&0x01)!=0);
        boolean useCoarse 	= ((gType&0x02)!=0);
        boolean useText 	= ((gType&0x04)!=0);
        boolean useChequer 	= ((gType&0x08)!=0);
        	
        // Draw foreground
        int i, j;
        
        if (useFine || useCoarse) {
    		
            // Set grid spacing
        	int delta;
           	delta = 10;            
            if (useCoarse)
            	delta *= 2;
            
            // Create grid path
            Path rgPath = new Path();
            
            if (useChequer) {
	            int flip = 0;
	        	for (j = yPadding; j<yPadding + dHeight; j+=delta) {
	        		for (i = flip*delta; i<dWidth; i+=delta*2.0f) {
	        			rgPath.addRect(i, j, i+delta, j+delta, Path.Direction.CW);
	        		}
	        		flip = (~flip)&0x01;
	        	}
            } else {
	        	for (j = yPadding; j<yPadding + dHeight; j+=delta) {
	        		for (i = 0; i<dWidth; i+=delta) {
	        			rgPath.addRect(i, j, i+delta, j+delta, Path.Direction.CW);
	        		}
	        	}            	
            }
        	
        	// Render path
            LineRender.renderPath(cFront, rgPath, wInfo.getGridCol(), wInfo.getGridStyle(), 0);
        }
        	
    	// Render grid text
    	if (useText) {
    		long t,dt;
    		int yPos = yPadding + (dHeight>>1);
    		int delta = 20;
    		
    		// Set paint
            Paint pt = new Paint(Paint.ANTI_ALIAS_FLAG);        		
            pt.setColor(wInfo.getTextCol());
            if (wInfo.is42() || wInfo.is22())
            	pt.setTextSize(15.0f);
            else
            	pt.setTextSize(11.0f);
            pt.setTextAlign(Align.CENTER);
    		
            // Set time
            t = wInfo.getSampRate()*dWidth/1000;
            dt = t*delta/dWidth;
            
            t -= dt;
        	for (i=delta;i<dWidth;i+=delta) {
        		cFront.save();
        		cFront.rotate(-90.0f, i+5, yPos);
        		cFront.drawText(timeText(t), i+5, yPos, pt);
        		cFront.restore();
        		t -= dt;
        	}        		
    	}       
	}	
	private void renderLine(Canvas c, int colour, int style, RenderInfo plot) {
		
        float px, py, dVal;
        
        if (plot.dataValue != null) {
            
            int st_type = LineRender.getStyleType(style);
            
            // Render data values to graph      
            rPath.rewind();
            if (plot.rMax != TokaplotService.maxVal && plot.rMin != TokaplotService.maxVal) { 
                
                // Normalisation constant
                float delta = plot.rMax - plot.rMin;
                if (delta == 0.0f)
                    delta = 1.0f;
                delta = (float)dHeight/delta;
                
                if (st_type == 1) {
                    
                    // Fill style - move to bottom right hand corner
                    px = (float)dWidth;
                    rPath.moveTo(px, (float)dHeight);
                    
                    // Join to first value projected onto right hand edge                   
                    py = yPadding + dHeight - (plot.dataFirstValue - plot.rMin)*delta;
                    if (py>dHeight) py = dHeight;
                    if (py<0.0f) py = 0.0f;
                    rPath.lineTo(px, py);                   
                } else {
                    
                    // Other styles - move to first value projected on right hand edge
                    px = (float)dWidth;
                    py = yPadding + dHeight - (plot.dataFirstValue - plot.rMin)*delta;
                    if (py>dHeight) py = dHeight;
                    if (py<0.0f) py = 0.0f;
                    rPath.moveTo(px, py);
                }
                
                // Render line
                if (st_type == 2) {                 
                    
                    // Bar style
                    for (int i = plot.nDataStart; i < plot.nDataStart+plot.nData; i++) {
                        
                        // Plot
                        dVal = plot.dataValue[i%plot.rMaxTime];
                        if (dVal > 0.0f) {
                            px = (float)dWidth - plot.dataTime[i%plot.rMaxTime];
                            py = yPadding + dHeight - (dVal - plot.rMin)*delta;
                            if (py>dHeight) py = dHeight;
                            if (py<0.0f) py = 0.0f;
                            rPath.moveTo(px, dHeight);
                            rPath.lineTo(px, py);
                        }                       
                    }
                } else {
                    
                    // Joined style
                    for (int i = plot.nDataStart; i < plot.nDataStart+plot.nData; i++) {
                        
                        // Plot
                        px = (float)dWidth - plot.dataTime[i%plot.rMaxTime];
                        py = yPadding + dHeight - (plot.dataValue[i%plot.rMaxTime] - plot.rMin)*delta;
                        if (py>dHeight) py = dHeight;
                        if (py<0.0f) py = 0.0f;
                        rPath.lineTo(px, py);
                        
                    }               
                }

                // Move to end
                if (st_type == 1) {
                    px = (float)dWidth - plot.dataLastTime;
                    py = yPadding + dHeight - (plot.dataLastValue - plot.rMin)*delta;
                    if (py>dHeight) py = dHeight;
                    if (py<0.0f) py = 0.0f;
                    rPath.lineTo(px, py);
                    rPath.lineTo(px, dHeight);
                }
                
            }
            
            // Render path
            LineRender.renderPath(c, rPath, colour, style, Paint.ANTI_ALIAS_FLAG);
        }
	}
	
    public String timeText(long ttime) {
    	long tsec = ttime%60;
    	long tmin = (ttime%3600)/60;
    	long thrs = (ttime%86400)/3600;
    	long tdays = ttime/86400;
        String infoText = "";
        if (tdays!=0)
        	infoText = infoText + tdays + " d ";
        if (thrs!=0)
        	infoText = infoText + thrs + " h ";
        if (tmin!=0)
        	infoText = infoText + tmin + " m ";
        if (tsec!=0)
        	infoText = infoText + tsec + " s ";
        return infoText;
    }
}
